<?xml version="1.0" encoding="UTF-8" ?>

<!-- Summary: Generates a scanner that works something like a finite automata !-->
<!-- Created: April 25, 2007 !-->

<xsl:stylesheet version="1.0"
			xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
			xmlns:xs="http://www.w3.org/2001/XMLSchema" >
<xsl:output method="text" encoding="UTF-8" indent="yes"/>
<xsl:preserve-space elements="*" />
<xsl:template match="/">
<xsl:apply-templates match="Automata"/>
</xsl:template>
<xsl:template match="Automata">
/* Automatically generated finite automoata style scanner */

using System;
using System.IO;

namespace GenDotNet.<xsl:value-of select="@namespaceName"/> {
	class <xsl:value-of select="@className"/>
	<xsl:if test="@baseClass"> : <xsl:value-of select="@baseClass"/> </xsl:if> {
    <!-- insert any explicit class level code -->
		<xsl:for-each select="ClassCode"><xsl:value-of select="."/></xsl:for-each>

    <!-- insert an enum of the different states -->
		<xsl:call-template name="StatesEnum"/>

    <!-- insert the run method, which runs the automata on the given string, and returns the result -->
		<xsl:call-template name="RunMethod"/>
	}
}
</xsl:template>

<!-- an enum containing all of the states of this automata -->
<xsl:template name="StatesEnum">
		enum FAState {
			<xsl:for-each select="State"><xsl:value-of select="@stateName"/>,
			</xsl:for-each>
		}
</xsl:template>

<!-- main method that runs the automata -->

<xsl:template name="RunMethod">
		public override string Run(string input) {
      <!-- insert any explicit code that needs to be run before the automata starts -->
      <xsl:for-each select="PreProcessCode"><xsl:value-of select="."/></xsl:for-each>
			StringWriter output=new StringWriter();
			FAState state=FAState.<xsl:value-of select="State[@initial='true']/@stateName"/>;
			int inputIndex=0;
			while(inputIndex &lt; input.Length) {
				char inputChar=input[inputIndex];
				int usedChars=1;
				switch(state) {
					<xsl:for-each select="State">
					case FAState.<xsl:value-of select="@stateName"/>:
						<xsl:apply-templates select="Trans"/>
            
            <!-- if there was no default transition defined, then return an empty string if none of the other transitions match -->
            <xsl:if test="not(Trans[not(@string) and not(@char) and not(@charSet) and not(@testMethod)])">
						return "";</xsl:if>

          </xsl:for-each>
				}
				inputIndex+=usedChars;
			}
			return output.ToString();
		}
</xsl:template>

<!-- write the code for each of the possible types of transitions -->
<xsl:template match="Trans">
	<xsl:choose>
		<xsl:when test="@string">
			<xsl:call-template name="TransString"/>
		</xsl:when>
		<xsl:when test="@char">
			<xsl:call-template name="TransChar"/>
		</xsl:when>
		<xsl:when test="@charSet">
			<xsl:call-template name="TransCharSet"/>
		</xsl:when>
		<xsl:when test="@testMethod">
			<xsl:call-template name="TransTestMethod"/>
		</xsl:when>
		<xsl:otherwise>
			<xsl:call-template name="TransDefault"/>
		</xsl:otherwise>
	</xsl:choose>
</xsl:template>

<!-- transition that just checks if the input char is equal to a given char -->
<xsl:template name="TransChar">
						if(inputChar=='<xsl:value-of select="@char"/>')
						{
							<xsl:value-of select="."/>
							<xsl:if test="@next">state=FAState.<xsl:value-of select="@next"/>;
							</xsl:if>
							<xsl:if test="not(@echo='false')">output.Write(inputChar);
							</xsl:if>
							<xsl:if test="@replace='true'">usedChars=0;
							</xsl:if>break;
						}</xsl:template>

<!-- transition that checks if the input char is in a given set of chars -->
<xsl:template name="TransCharSet">
						if(&quot;<xsl:value-of select="@charSet"/>&quot;.IndexOf(inputChar)!=-1)
						{
							<xsl:value-of select="."/>
							<xsl:if test="@next">state=FAState.<xsl:value-of select="@next"/>;
							</xsl:if>
							<xsl:if test="not(@echo='false')">output.Write(inputChar);
							</xsl:if>
							<xsl:if test="@replace='true'">usedChars=0;
							</xsl:if>break;
						}</xsl:template>
							
<!-- transition that calls a specified method, with the input char as the only argument, and matches if the method returns true -->
<xsl:template name="TransTestMethod">
						if(<xsl:value-of select="@testMethod"/>(inputChar))
						{
							<xsl:value-of select="."/>
							<xsl:if test="@next">state=FAState.<xsl:value-of select="@next"/>;
							</xsl:if>
							<xsl:if test="not(@echo='false')">output.Write(inputChar);
							</xsl:if>
							<xsl:if test="@replace='true'">usedChars=0;
							</xsl:if>break;
						}</xsl:template>
							
<!-- transition that checks if a given string matches with the input string at this position.
If it matches, it will consume the whole string (unless "replace" is set).-->
<xsl:template name="TransString">
						{
							<xsl:value-of select="."/>
							string targetString=&quot;<xsl:value-of select="@string"/>&quot;;
							if(inputIndex+targetString.Length&lt;=input.Length&amp;&amp;input.Substring(inputIndex,targetString.Length)==targetString)
							{
								<xsl:if test="@next">state=FAState.<xsl:value-of select="@next"/>;
								</xsl:if>
								<xsl:if test="not(@echo='false')">output.Write(targetString);
								</xsl:if>
								<xsl:choose>
									<xsl:when test="@replace='true'">usedChars=0;
								</xsl:when>
									<xsl:otherwise>usedChars=targetString.Length;
								</xsl:otherwise>
								</xsl:choose>break;
							}
						}</xsl:template>
							
<!-- a default transition that always matches -->
<xsl:template name="TransDefault">
						<xsl:value-of select="."/>
						<xsl:if test="@next">
						state=FAState.<xsl:value-of select="@next"/>;</xsl:if>
						<xsl:if test="not(@echo='false')">
						output.Write(inputChar);</xsl:if>
						<xsl:if test="@replace='true'">
						usedChars=0;</xsl:if>
						break;</xsl:template>

</xsl:stylesheet>

